﻿using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using RoMBot.MemoryLib;

namespace RoMBot.Classes
{
    [StructLayout( LayoutKind.Explicit )]
    public class Camera
    {
        public const float YAngle = 35.0f;

        public Camera()
        {
        }

        public Camera( int address )
        {
            this.Address = address;
            this.Update();
        }

        public void Update()
        {
            if ( Address == 0 )
            {
                return;
            }
            var i = new IntPtr( Bot.Instancia.ProcId );
            var s = new Stopwatch();
            s.Start();
            var c = MemoryLib.SMemory.ReadObject( Memory.ProcHandle , ( uint ) this.Address, typeof( Camera ) );
            s.Stop();
            var a = s.Elapsed.TotalMilliseconds;

            s.Restart();
            this.XUVec = Memory.ReadFloat( this.Address + Addresses.camXUVec_offset );
            this.YUVec = Memory.ReadFloat( this.Address + Addresses.camYUVec_offset );
            this.ZUVec = Memory.ReadFloat( this.Address + Addresses.camZUVec_offset );

            // camera coordinates
            this.X = Memory.ReadFloat( this.Address + Addresses.camX_offset );
            this.Y = Memory.ReadFloat( this.Address + Addresses.camY_offset );
            this.Z = Memory.ReadFloat( this.Address + Addresses.camZ_offset );

            // camera focus coordinates
            this.XFocus = Memory.ReadFloat( this.Address + Addresses.camXFocus_offset );
            this.ZFocus = Memory.ReadFloat( this.Address + Addresses.camZFocus_offset );
            this.YFocus = Memory.ReadFloat( this.Address + Addresses.camYFocus_offset );

            // camera distance
            this.Distance = Memory.ReadFloatPtr( Addresses.staticbase_char, new[] { Addresses.camDistance_offset1, Addresses.camDistance_offset2 } );

            s.Stop();
            var b = s.Elapsed.TotalMilliseconds;
        }

        public void SetPosition( float x, float y, float z )
        {
            if ( !Bot.ControlCamera )
            {
                return;
            }
            this.XUVec = x;
            this.YUVec = y;
            //self.ZUVec = z;

            Memory.WriteFloat( this.Address + Addresses.camXUVec_offset, x );
            Memory.WriteFloat( this.Address + Addresses.camYUVec_offset, y );
        }

        public void SetRotation( float angle )
        {
            if ( !Bot.ControlCamera )
            {
                return;
            }
            Update();

            // y vector
            var playerYAngle = ( float ) Bot.Player.DirectionY;
            if ( playerYAngle > 0.35 )
            {
                playerYAngle = 0.35f;
            }
            else if ( playerYAngle < -0.35 )
            {
                playerYAngle = -0.35f;
            }
            var vec3 = ( float ) Math.Sin( YAngle - playerYAngle ) * this.Distance;

            // x and z vectors
            var hypotenuse = ( float ) Math.Pow( ( float ) Math.Pow( this.Distance, 2f ) - ( float ) Math.Pow( vec3, 2f ), .5f );

            var vec1 = ( float ) Math.Cos( angle + Math.PI ) * hypotenuse;
            var vec2 = ( float ) Math.Sin( angle + Math.PI ) * hypotenuse;

            // new camera coordinates
            var nx = this.XFocus + vec1;
            var nz = this.ZFocus + vec2;
            var ny = this.YFocus + vec3;

            Memory.WriteFloat( this.Address + Addresses.camX_offset, nx );
            Memory.WriteFloat( this.Address + Addresses.camZ_offset, nz );
            Memory.WriteFloat( this.Address + Addresses.camY_offset, ny );
        }

        public void SetDistance( float distance )
        {
            if ( !Bot.ControlCamera )
            {
                return;
            }
            if ( distance < 1.0f )
            {
                distance = 1.0f;
            }
            else if ( distance > 150.0f )
            {
                distance = 150.0f;
            }

            // Change distance
            Memory.WriteFloatPtr( Addresses.staticbase_char, new[] { Addresses.camDistance_offset1, Addresses.camDistance_offset2 }, distance );

            // Save distance setting
            Memory.WriteFloatPtr( Addresses.staticbase_char, Addresses.camDistanceSave_offset, distance );
        }

        [FieldOffset( 0x454 )]
        public float Distance;

        [FieldOffset( 0x110 )]
        public float XFocus;

        [FieldOffset( 0x114 )]
        public float YFocus;

        [FieldOffset( 0x118 )]
        public float ZFocus;

        [FieldOffset( 0x104 )]
        public float X;

        [FieldOffset( 0x108 )]
        public float Y;

        [FieldOffset( 0x10C )]
        public float Z;

        [FieldOffset( 0x128 )]
        public float XUVec;

        [FieldOffset( 0x12C )]
        public float YUVec;

        [FieldOffset( 0x130 )]
        public float ZUVec;

        [FieldOffset( 0 )]
        public int Address;
    }
}
